package com.vcredit.creditcat.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.springframework.util.Base64Utils;

public class RSAUtil {
  public static final String KEY_ALGORITHM = "RSA";
  public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
  public static final String SIGNATURE_ALGORITHM_SHA = "SHA256WithRSA";
  public static final String ENCODE_ALGORITHM = "SHA-256";
  private static final String PUBLIC_KEY = "RSAPublicKey";
  private static final String PRIVATE_KEY = "RSAPrivateKey";
  /** */
  /** 
  * RSA最大加密明文大小 
  */
  private static final int MAX_ENCRYPT_BLOCK = 117;

  /** */
  /** 
  * RSA最大解密密文大小 
  */
  private static final int MAX_DECRYPT_BLOCK = 128;

  /**
   * bytes[]换成16进制字符串
   *
   * @param src
   * @return
   */
  public static String bytesToHexString(byte[] src) {
    StringBuilder stringBuilder = new StringBuilder("");
    if (src == null || src.length <= 0) {
      return null;
    }
    for (int i = 0; i < src.length; i++) {
      int v = src[i] & 0xFF;
      String hv = Integer.toHexString(v);
      if (hv.length() < 2) {
        stringBuilder.append(0);
      }
      stringBuilder.append(hv);
    }
    return stringBuilder.toString();
  }

  /** 
   * 签名 
   * @param data 待签名数据 
   * @param privateKey 商户私钥 
   * @param algorithm 签名算法
   * @return 签名值 
   */
  public static String sign(byte[] data, String privateKey, String algorithm) throws Exception {
    MessageDigest messageDigest;
    byte[] signed;
    try {
      // 解密由base64编码的私钥
      byte[] keyBytes = Base64Utils.decodeFromString(privateKey);

      messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM);
      messageDigest.update(data);
      byte[] outputDigest_sign = messageDigest.digest();
      System.out.println("SHA-256加密后-----》" + bytesToHexString(outputDigest_sign));
      Signature Sign = Signature.getInstance(algorithm);

      PKCS8EncodedKeySpec pcks8 = new PKCS8EncodedKeySpec(keyBytes);
      // KEY_ALGORITHM 指定的加密算法
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      // 取私钥匙对象
      PrivateKey priKey = keyFactory.generatePrivate(pcks8);

      Sign.initSign(priKey);
      Sign.update(outputDigest_sign);
      signed = Sign.sign();
      System.out.println("SHA256withRSA签名后-----》" + bytesToHexString(signed));

      return encryptBASE64(signed);

    } catch (Exception e) {
      System.out.println(e);
    }
    return null;
  }


  /** 
  * RSA验签名检查 
  * @param data 待签名数据
  * @param sign 签名值 
  * @param publicKey 分配给开发商公钥 
  * @param algorithm 签名算法
  * @return 布尔值 
  */
  public static boolean verifySign(byte[] data, byte[] sign, String publicKey, String algorithm) {
    MessageDigest messageDigest;
    boolean SignedSuccess;

    try {
      // 解密由base64编码的私钥
      byte[] keyBytes = Base64Utils.decodeFromString(publicKey);
      // KEY_ALGORITHM 指定的加密算法
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      // 构造PKCS8EncodedKeySpec对象
      X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
      // 取公钥对象
      PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);

      messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM);
      messageDigest.update(data);
      byte[] outputDigest_verify = messageDigest.digest();
      System.out.println("SHA-256加密后-----》" + bytesToHexString(outputDigest_verify));

      Signature signature = Signature.getInstance(algorithm);
      signature.initVerify(pubKey);
      signature.update(data);
      SignedSuccess = signature.verify(sign);
      return SignedSuccess;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }

  /** 
   * 签名 
   * @param data 待签名数据 
   * @param privateKey 商户私钥 
   * @return 签名值 
   */
  public static String sign(byte[] data, String privateKey) throws Exception {
    // 解密由base64编码的私钥
    byte[] keyBytes = decryptBASE64(privateKey);

    // 构造PKCS8EncodedKeySpec对象
    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

    // KEY_ALGORITHM 指定的加密算法
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

    // 取私钥匙对象
    PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

    // 用私钥对信息生成数字签名
    Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
    signature.initSign(priKey);
    signature.update(data);
    System.err.println(new String(signature.sign()));
    return encryptBASE64(signature.sign());
  }

  public static byte[] decryptBASE64(String privateKey) throws IOException {
    byte[] arr = Base64.decodeBase64(privateKey.getBytes("UTF-8"));
    return arr;
  }

  public static String encryptBASE64(byte[] encoded) {
    String requestValue = Base64.encodeBase64String(encoded);
    return requestValue;
  }

  public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {

    // 解密由base64编码的公钥
    byte[] keyBytes = decryptBASE64(publicKey);

    // 构造X509EncodedKeySpec对象
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

    // KEY_ALGORITHM 指定的加密算法
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

    // 取公钥匙对象
    PublicKey pubKey = keyFactory.generatePublic(keySpec);

    Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
    signature.initVerify(pubKey);
    signature.update(data);

    // 验证签名是否正常
    return signature.verify(decryptBASE64(sign));
  }

  /**
   * 私钥解密
   * @param data
   * @param key
   * @return
   * @throws Exception
   */
  public static String decryptByPrivateKey(byte[] data, String key) throws Exception {
    byte[] keyBytes = decryptBASE64(key);
    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(Cipher.DECRYPT_MODE, privateK);
    int inputLen = data.length;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    int offSet = 0;
    byte[] cache;
    int i = 0;
    // 对数据分段解密  
    while (inputLen - offSet > 0) {
      if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
        cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
      } else {
        cache = cipher.doFinal(data, offSet, inputLen - offSet);
      }
      out.write(cache, 0, cache.length);
      i++;
      offSet = i * MAX_DECRYPT_BLOCK;
    }
    byte[] decryptedData = out.toByteArray();
    out.close();
    return new String(decryptedData, "utf-8");
  }

  /**
   * 公钥解密
   * @param data
   * @param key
   * @return
   * @throws Exception
   */
  public static String decryptByPublicKey(byte[] data, String key) throws Exception {
    byte[] keyBytes = decryptBASE64(key);
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    Key publicK = keyFactory.generatePublic(x509KeySpec);
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(Cipher.DECRYPT_MODE, publicK);
    int inputLen = data.length;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    int offSet = 0;
    byte[] cache;
    int i = 0;
    // 对数据分段解密  
    while (inputLen - offSet > 0) {
      if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
        cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
      } else {
        cache = cipher.doFinal(data, offSet, inputLen - offSet);
      }
      out.write(cache, 0, cache.length);
      i++;
      offSet = i * MAX_DECRYPT_BLOCK;
    }
    byte[] decryptedData = out.toByteArray();
    out.close();
    return new String(decryptedData, "utf-8");
  }

  /**
   * 公钥加密
   * @param data
   * @param key
   * @return
   * @throws Exception
   */
  public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
    byte[] keyBytes = decryptBASE64(key);
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    Key publicK = keyFactory.generatePublic(x509KeySpec);
    // 对数据加密  
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(Cipher.ENCRYPT_MODE, publicK);
    int inputLen = data.length;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    int offSet = 0;
    byte[] cache;
    int i = 0;
    // 对数据分段加密  
    while (inputLen - offSet > 0) {
      if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
        cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
      } else {
        cache = cipher.doFinal(data, offSet, inputLen - offSet);
      }
      out.write(cache, 0, cache.length);
      i++;
      offSet = i * MAX_ENCRYPT_BLOCK;
    }
    byte[] encryptedData = out.toByteArray();
    out.close();
    return encryptedData;
  }

  /**
   * 私钥加密
   * @param data
   * @param key
   * @return
   * @throws Exception
   */
  public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
    byte[] keyBytes = decryptBASE64(key);
    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
    Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
    cipher.init(Cipher.ENCRYPT_MODE, privateK);
    int inputLen = data.length;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    int offSet = 0;
    byte[] cache;
    int i = 0;
    // 对数据分段加密  
    while (inputLen - offSet > 0) {
      if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
        cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
      } else {
        cache = cipher.doFinal(data, offSet, inputLen - offSet);
      }
      out.write(cache, 0, cache.length);
      i++;
      offSet = i * MAX_ENCRYPT_BLOCK;
    }
    byte[] encryptedData = out.toByteArray();
    out.close();
    return encryptedData;
  }

  public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
    Key key = (Key) keyMap.get(PRIVATE_KEY);

    return encryptBASE64(key.getEncoded());
  }

  public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
    Key key = (Key) keyMap.get(PUBLIC_KEY);

    return encryptBASE64(key.getEncoded());
  }

  public static String[] getPublicAndPrivateKey() throws Exception {
    Map<String, Object> keyMap = RSAUtil.initKey();
    return new String[] {getPublicKey(keyMap), getPrivateKey(keyMap)};
  }

  public static Map<String, Object> initKey() throws Exception {
    KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
    keyPairGen.initialize(1024);

    KeyPair keyPair = keyPairGen.generateKeyPair();

    // 公钥
    RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

    // 私钥
    RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

    Map<String, Object> keyMap = new HashMap<String, Object>(2);

    keyMap.put(PUBLIC_KEY, publicKey);
    keyMap.put(PRIVATE_KEY, privateKey);
    return keyMap;
  }
}
